
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
 */
package be.ac.ua.pats.thesisrobotica.device.parser;

//~--- non-JDK imports --------------------------------------------------------

import be.ac.ua.pats.rr.entity.AbstractSensor;
import be.ac.ua.pats.thesisrobotica.device.NXTRobot;
import be.ac.ua.pats.thesisrobotica.device.exceptions.DuplicateException;
import be.ac.ua.pats.thesisrobotica.device.exceptions.InitializationException;
import be.ac.ua.pats.thesisrobotica.device.robotAnalysis.Grapher;
import be.ac.ua.pats.thesisrobotica.device.sensors.ReflectiveSensor;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

//~--- JDK imports ------------------------------------------------------------

import java.io.IOException;
import java.io.StringReader;

import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Queue;
import java.util.StringTokenizer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * @author Jimmy
 */
public class MessageParser extends Observable implements Runnable {
    Queue<String>                     messageQueue = new LinkedList<String>();
    boolean                           initialized  = false;
    NXTRobot                          nxtRobot;
    Hashtable<String, AbstractSensor> sensorList;
    StringTokenizer                   tokenizer;

    public MessageParser() {
        super();
    }

    public MessageParser(NXTRobot entity) {
        this.nxtRobot = entity;
    }

    public void setSensors(Hashtable<String, AbstractSensor> sensorList) {
        this.sensorList = sensorList;
        initialized     = true;
    }

    /**
     * Adds a message (that needs to be parsed) to the queue
     *
     * @param message The message that needs to be added to the queue
     */
    public void addMessage(String message) {
        messageQueue.add(message);
    }

    /**
     * Parses a message:
     * 1. First checks wether is a notification by means of parsing it
     * with a stringtokenizer
     * 2. Otherwise, parses it (considering it will be an xml-file)
     *
     * @param message The message to be parsed
     */
    private synchronized void parse(String message)
            throws ParserConfigurationException, SAXException, IOException, InitializationException,
                   DuplicateException {
        tokenizer = new StringTokenizer(message, ",");

        String type = tokenizer.nextToken();

        System.out.println("Received type: " + type);

        if (type.equals("V")) {
            if (initialized) {
                System.out.println("Received a value");

                String id    = tokenizer.nextToken();
                String value = tokenizer.nextToken();

                AbstractSensor sensor = sensorList.get(id);
                if(sensor instanceof ReflectiveSensor) {
                    ((ReflectiveSensor)sensor).setNewValue(value);
                }

                return;
            } else {
                throw new InitializationException();
            }
        } else if (type.equals("RID")) {
            System.out.println("Received ID from robot");
            nxtRobot.setRobotId(tokenizer.nextToken());
        } else if (type.equals("SID")) {
            System.out.println("Received ID from all sensors");
            System.out.println(tokenizer.countTokens());

            while (tokenizer.hasMoreTokens()) {
                String id         = tokenizer.nextToken();
                System.out.println(id);
                if(!id.equals("")) {
                    String sensorType = tokenizer.nextToken();

                    nxtRobot.addSensorName(id);
                }
            }

            nxtRobot.finalizeSensorList();
        } else {
            parseBenchmark(message);
        }
    }

    public void parseBenchmark(String message) throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory dbf      = DocumentBuilderFactory.newInstance();
        DocumentBuilder        db       = dbf.newDocumentBuilder();
        InputSource            inStream = new InputSource();

        inStream.setCharacterStream(new StringReader(message));

        Document doc        = db.parse(inStream);
        Element  docElement = doc.getDocumentElement();
        String   rootName   = docElement.getTagName();

        if (rootName.equals("Benchmark")) {
            NodeList     sensorList2 = docElement.getElementsByTagName("Sensor");
            Node         sensor      = sensorList2.item(0);
            NamedNodeMap attributes  = sensor.getAttributes();
            String       name        = attributes.item(0).getNodeValue();
            NodeList     valueList   = sensor.getChildNodes();
            Node         value;
            double[][]   valueArray = new double[valueList.getLength()][valueList.getLength()];

            for (int i = 0; i < valueArray[0].length; i++) {
                value = valueList.item(i);

                NodeList coordinateList = value.getChildNodes();

                valueArray[1][i] = Double.parseDouble(coordinateList.item(0).getTextContent());
                valueArray[0][i] = Double.parseDouble(coordinateList.item(1).getTextContent());
            }

            Grapher.draw(name, "Time (ms)", "Battery voltage (mV)", valueArray);
        }
    }

    /**
     * Will constantly check wether a new message is available to be parsed
     */
    public void run() {
        String newMessage = "";

        while (true) {    // @todo: while(true) = big no-no
            newMessage = messageQueue.poll();

            if (!(newMessage == null)) {
                try {
                    try {
                        this.parse(newMessage);
                    } catch (DuplicateException ex) {
                        ex.printStackTrace();
                    }
                } catch (ParserConfigurationException e) {
                    e.printStackTrace();
                } catch (SAXException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InitializationException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
